package org.zjvis.datascience.web.controller;

import cn.hutool.core.util.CharUtil;
import cn.weiguangfu.swagger2.plus.annotation.ApiPlus;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.api.client.util.Lists;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.groovy.util.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.zjvis.datascience.common.dto.DatasetCategoryDTO;
import org.zjvis.datascience.common.dto.dataset.DatasetJsonInfo;
import org.zjvis.datascience.common.dto.user.UserDTO;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.graph.importer.ImporterCSV;
import org.zjvis.datascience.common.graph.model.GraphDataCheckException;
import org.zjvis.datascience.common.graph.model.GraphDataErrorCode;
import org.zjvis.datascience.common.graph.model.GraphFileFormatEnum;
import org.zjvis.datascience.common.graph.util.GraphFileUtil;
import org.zjvis.datascience.common.model.ApiResult;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.util.StringUtil;
import org.zjvis.datascience.common.vo.gis.GisFileWriteDTO;
import org.zjvis.datascience.service.DatasetCategoryService;
import org.zjvis.datascience.service.MinioService;
import org.zjvis.datascience.service.UserService;
import org.zjvis.datascience.service.dataset.DatasetService;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.List;

@ApiPlus(value = true)
@RequestMapping("/graphFile")
@RestController
@Api(tags = "graphFile", description = "图文件管理接口")
@Validated
public class GraphFileController {

    @Autowired
    private UserService userService;

    @Autowired
    private MinioService minioService;

    @Autowired
    private DatasetCategoryService datasetCategoryService;

    @Autowired
    private DatasetService datasetService;

    public static String GRAPH_FILE_DIR = "graph-file";

    @PostMapping("/uploadFile")
    @ApiOperation(value = "图文件上传", notes = "图文件上传")
    public ApiResult<String> uploadFile(@Valid GisFileWriteDTO gisFileWriteDTO) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (null == gisFileWriteDTO.getCategoryId()){
            gisFileWriteDTO.setCategoryId(datasetCategoryService.queryCategoryIdByUserIdAndCategoryName(user.getId(), datasetCategoryService.getDefaultGraphCategory()));
            if (gisFileWriteDTO.getCategoryId() < 0){
                gisFileWriteDTO.setCategoryId(datasetCategoryService.addDefaultFolder(datasetCategoryService.getDefaultGraphCategory()).getCategoryId());
            }
        }
        try {
            GraphFileUtil.checkGraphFile(gisFileWriteDTO.getFile().getInputStream(), gisFileWriteDTO.getFile().getOriginalFilename());
        } catch (GraphDataCheckException | DataScienceException e) {
            throw e;
        } catch (Exception e) {
            throw new GraphDataCheckException(GraphDataErrorCode.GRAPH_FILE_PARSE_ERROR);
        }
        String userName = user.getName();
        String fileName = gisFileWriteDTO.getFile().getOriginalFilename();
        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");
        int i = 1;
        String ext = FilenameUtils.getExtension(fileName);
        String baseName = FilenameUtils.removeExtension(fileName);
        while (objectNames.contains(fileName)) {
            fileName = baseName + CharUtil.UNDERLINE + i++ + CharUtil.DOT + ext;
        }
        minioService.putObject(GRAPH_FILE_DIR, userName + "/" + fileName, gisFileWriteDTO.getFile().getInputStream());
        DatasetJsonInfo datasetJsonInfo = DatasetJsonInfo.builder()
                .type("graph-" + ext)
                .table(fileName)
                .location(GRAPH_FILE_DIR + "/" + userName + "/" + fileName)
                .build();
        datasetService.saveDataset(user.getId(), gisFileWriteDTO.getCategoryId(), fileName, datasetJsonInfo);
        return ApiResult.valueOf(fileName);
    }

    @PostMapping("/previewFile")
    @ApiOperation(value = "图文件预览", notes = "图文件预览")
    public ApiResult<JSONObject> previewFile(@Valid @NotNull @RequestParam("file") MultipartFile file) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String fileName = file.getOriginalFilename().toLowerCase();
        if (!fileName.endsWith(".csv")&&!fileName.endsWith(".json")&&!fileName.endsWith(".gml")) {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        JSONObject ret = new JSONObject();
        String format;
        Object content;
        InputStream in = file.getInputStream();
        if (fileName.endsWith(".json")) {
            format = GraphFileFormatEnum.JSON.getDesc();
            content = StringUtil.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else if (fileName.endsWith(".csv")) {
            format = GraphFileFormatEnum.CSV.getDesc();
            content = ImporterCSV.parseCSV2JSON(in, 20);
        } else if (fileName.endsWith(".gml")) {
            format = GraphFileFormatEnum.GML.getDesc();
            content = StringUtil.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        ret.put("format", format);
        ret.put("content", content);
        return ApiResult.valueOf(ret);
    }

    @PostMapping("/listFiles")
    @ApiOperation(value = "图文件列表", notes = "图文件列表")
    public ApiResult<List<String>> listFiles(HttpServletRequest request) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");
        return ApiResult.valueOf(objectNames);
    }

    @PostMapping("/updateFileName")
    @ApiOperation(value = "图文件重命名", notes = "图文件重命名")
    public ApiResult<List<String>> updateFileName(HttpServletRequest request,
                                                  @RequestBody
                                                          JSONObject params) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        String userName = user.getName();
        String fileName = params.getString("fileName");
        String updateName = params.getString("update");
        Long datasetId = params.getLong("datasetId");

        if (StringUtils.isEmpty(fileName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }

        if (StringUtils.isEmpty(updateName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }

        if (!FilenameUtils.getExtension(fileName).equals(FilenameUtils.getExtension(updateName))) {
            throw new DataScienceException(BaseErrorCode.DATASET_GRAPH_FILE_EXT_CHANGE);
        }

        if (fileName.equals(updateName)) {
            return ApiResult.valueOf(ApiResultCode.SUCCESS);
        }

        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");

        if (!objectNames.contains(fileName)) {
            return ApiResult.valueOf(ApiResultCode.DATA_NULL);
        }

        if (objectNames.contains(updateName)) {
            throw new DataScienceException(BaseErrorCode.DATASET_NAME_DUPLICATE_ERROR);
        }

        InputStream in = minioService.getObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        minioService.putObject(GRAPH_FILE_DIR, userName + "/" + updateName, in);
        minioService.deleteObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        datasetService.updateGraphItem(datasetId, updateName);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping("/deleteFile")
    @ApiOperation(value = "图文件删除", notes = "图文件删除")
    public ApiResult<Void> deleteFile(HttpServletRequest request, @RequestBody JSONObject params) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        String fileName = params.getString("fileName");
        if (StringUtils.isEmpty(fileName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }
        Long datasetId = params.getLong("datasetId");
        if (null == datasetId){
            datasetId = datasetService.queryByUserAndName(user.getId(), fileName);
        }
        datasetService.deleteById(datasetId);
        minioService.deleteObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    @PostMapping("/queryFileByName")
    @ApiOperation(value = "图文件预览", notes = "图文件预览")
    public ApiResult<JSONObject> queryFileByName(HttpServletRequest request, @RequestBody JSONObject params) throws Exception {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if (user == null || StringUtils.isEmpty(user.getName())) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR, null);
        }
        String userName = user.getName();
        String fileName = params.getString("fileName");
        if (StringUtils.isEmpty(fileName)) {
            return ApiResult.valueOf(ApiResultCode.PARAM_ERROR);
        }

        List<String> objectNames = minioService.listObjects(GRAPH_FILE_DIR, userName + "/");

        if (!objectNames.contains(fileName)) {
            return ApiResult.valueOf(ApiResultCode.DATA_NULL);
        }

        JSONObject ret = new JSONObject();
        String format;
        Object content;
        InputStream in = minioService.getObject(GRAPH_FILE_DIR, userName + "/" + fileName);
        if (fileName.endsWith(".json")) {
            format = GraphFileFormatEnum.JSON.getDesc();
            content = StringUtil.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else if (fileName.endsWith(".csv")) {
            format = GraphFileFormatEnum.CSV.getDesc();
            content = ImporterCSV.parseCSV2JSON(in, 20);
        } else if (fileName.endsWith(".gml")) {
            format = GraphFileFormatEnum.GML.getDesc();
            content = StringUtil.readInputStreamToString(in, StandardCharsets.UTF_8);
        } else {
            return ApiResult.valueOf(ApiResultCode.GRAPH_LOAD_FORMAT_ERROR);
        }
        ret.put("format", format);
        ret.put("content", content);
        return ApiResult.valueOf(ret);
    }

    @PostMapping("/genSQL")
    @ApiOperation(value = "图文件预览", notes = "图文件预览")
    public ApiResult<List<String>> genSQL() throws Exception {
        List<String> itemNames = minioService.listObjects(GRAPH_FILE_DIR);
        List<String> result = Lists.newArrayList();
        String sqlTemplate = "INSERT INTO `dataset` (name, user_id, category_id, data_json, gmt_create, gmt_modify, gmt_modifier, gmt_creator) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s');";
        for (String path: itemNames){
            String userName = path.split("/")[0];
            String fileName = path.split("/")[1];
            String[] s = fileName.split("\\.");
            UserDTO owner = userService.queryByName(userName);
            try {
                Long categoryId = datasetCategoryService.queryCategoryIdByUserIdAndCategoryName(owner.getId(), DatasetCategoryService.DEFAULT_CATEGORY);
                if (categoryId == -1){
                    List<DatasetCategoryDTO> existCategories = datasetCategoryService.queryByUserId(owner.getId());
                    if(null != existCategories){
                        categoryId = existCategories.get(0).getId();
                    }else {
                        break;
                    }
                }
                JSONObject jsonObject = new JSONObject(Maps.of("location", "/graph-file/" + path, "table", fileName, "type", "graph-" + s[1]));
                result.add(String.format(sqlTemplate, fileName, owner.getId(), categoryId, JSON.toJSONString(jsonObject), LocalDate.now().toString(), LocalDate.now().toString(), owner.getId(), owner.getId()));
            }catch (Exception e){
                System.out.println(path + "----------> " + fileName);
            }
        }
        return ApiResult.valueOf(result);
    }
}
